Add new function 'file-name-split'
authorLars Ingebrigtsen <larsi@gnus.org>
Tue, 9 Nov 2021 23:26:32 +0000 (00:26 +0100)
committerLars Ingebrigtsen <larsi@gnus.org>
Tue, 9 Nov 2021 23:26:32 +0000 (00:26 +0100)
* doc/lispref/files.texi (File Name Components): Document it.
* lisp/files.el (file-name-split): New function (bug#50572).

* lisp/emacs-lisp/shortdoc.el (file-name): Mention it.

doc/lispref/files.texi
etc/NEWS
lisp/emacs-lisp/shortdoc.el
lisp/files.el
test/lisp/files-tests.el

index ddc1d05c1cadf6431573165268bc35b28ea03f46..dd058b1215822bf3d3225d75497d027526c6a5e5 100644 (file)
@@ -2244,6 +2244,19 @@ and @code{file-name-nondirectory}.  For example,
 @end example
 @end defun
 
+@defun file-name-split filename
+This function splits a file name into its components, and can be
+thought of as the inverse of @code{string-joing} with the appropriate
+directory separator.  For example,
+
+@example
+(file-name-split "/tmp/foo.txt")
+    @result{} ("" "tmp" "foo.txt")
+(string-join (file-name-split "/tmp/foo.txt") "/")
+    @result{} "/tmp/foo.txt"
+@end example
+@end defun
+
 @node Relative File Names
 @subsection Absolute and Relative File Names
 @cindex absolute file name
index 807f31fa33ed066e5d1f3ab801cac734a1f460c9..3cad0995ac5449e3c153efd04f023bd95919c399 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -612,6 +612,10 @@ Use 'exif-parse-file' and 'exif-field' instead.
 \f
 * Lisp Changes in Emacs 29.1
 
++++
+*** New function 'file-name-split'.
+This returns a list of all the components of a file name.
+
 +++
 *** New macro 'with-undo-amalgamate'
 It records a particular sequence of operations as a single undo step
index c3d6c7429403f3179b41cb912943ba94c81ddb75..a9f548b104eb411c36eda299e562d28d80654c6d 100644 (file)
@@ -281,6 +281,9 @@ There can be any number of :example/:result elements."
    :eval (file-name-base "/tmp/foo.txt"))
   (file-relative-name
    :eval (file-relative-name "/tmp/foo" "/tmp"))
+  (file-name-split
+   :eval (file-name-split "/tmp/foo")
+   :eval (file-name-split "foo/bar"))
   (make-temp-name
    :eval (make-temp-name "/tmp/foo-"))
   (file-name-concat
index 0bc7a92fbea4e96adbcc50cc3ffe34d4af4216b1..c694df38268d9efa5a80fd7aa88ca7f122b57b34 100644 (file)
@@ -5051,6 +5051,29 @@ See also `file-name-sans-extension'."
   (file-name-sans-extension
    (file-name-nondirectory (or filename (buffer-file-name)))))
 
+(defun file-name-split (filename)
+  "Return a list of all the components of FILENAME.
+On most systems, this will be true:
+
+  (equal (string-join (file-name-split filename) \"/\") filename)"
+  (let ((components nil))
+    ;; If this is a directory file name, then we have a null file name
+    ;; at the end.
+    (when (directory-name-p filename)
+      (push "" components)
+      (setq filename (directory-file-name filename)))
+    ;; Loop, chopping off components.
+    (while (length> filename 0)
+      (push (file-name-nondirectory filename) components)
+      (let ((dir (file-name-directory filename)))
+        (setq filename (and dir (directory-file-name dir)))
+        ;; If there's nothing left to peel off, we're at the root and
+        ;; we can stop.
+        (when (equal dir filename)
+          (push "" components)
+          (setq filename nil))))
+    components))
+
 (defcustom make-backup-file-name-function
   #'make-backup-file-name--default-function
   "A function that `make-backup-file-name' uses to create backup file names.
index c6d7c19279b2b751e5c0762c6780868f59c24cc3..1e20317739ad0c501f163e5e2c3aaae93d1ceb3f 100644 (file)
@@ -1800,6 +1800,12 @@ Prompt users for any modified buffer with `buffer-offer-save' non-nil."
          ;; `save-some-buffers-default-predicate' (i.e. the 2nd element) is ignored.
          (nil save-some-buffers-root ,nb-might-save))))))
 
+(defun test-file-name-split ()
+  (should (equal (file-name-split "foo/bar") '("foo" "bar")))
+  (should (equal (file-name-split "/foo/bar") '("" "foo" "bar")))
+  (should (equal (file-name-split "/foo/bar/zot") '("" "foo" "bar" "zot")))
+  (should (equal (file-name-split "/foo/bar/") '("" "foo" "bar" "")))
+  (should (equal (file-name-split "foo/bar/") '("foo" "bar" ""))))
 
 (provide 'files-tests)
 ;;; files-tests.el ends here